﻿// Fill out your copyright notice in the Description page of Project Settings.


#include "CurveWrappers/FloatCurveWrapper.h"
#include "CurveWrappers/FloatCurveInitParam.h"

bool UFloatCurveWrapper::Init(const FString& InCurveAssetPath, const FFloatCurveInitParam& InInitParam)
{
	// 根据送入的 CurveAssetPath 加载 CurveAsset
	auto CurvePtr = LoadObject<UCurveFloat>(this, *InCurveAssetPath);
	if (!ensureMsgf(nullptr != CurvePtr, TEXT("CurvePtr is not valid!")))
	{
		return false;
	}

	return Init(CurvePtr, InInitParam);
}

bool UFloatCurveWrapper::Init(const UCurveFloat* InCurvePtr, const FFloatCurveInitParam& InInitParam)
{
	// 进行入参的合法性检查
	if (!ensureMsgf(nullptr != InCurvePtr, TEXT("InCurvePtr is not valid!")))
	{
		return false;
	}

	// 考虑到曲线资产的复用性，本函数会根据入参的数值上下限，对曲线进行数值范围的调整。
	// 因此为了避免对原始的 InCurvePtr 造成修改，需要把 InCurvePtr 克隆一份，并赋值给 CurveAsset
	CurveAsset = Cast<UCurveFloat>(StaticDuplicateObject(InCurvePtr, this));
	if (!ensureMsgf(nullptr != CurveAsset, TEXT("CurveAsset clone failed!")))
	{
		return false;
	}

	bool bSuccess = false;
	switch (InInitParam.InitType)
	{
	case FFloatCurveInitParam::EInitType::InitWithoutChange:
		bSuccess = InitWithoutChange();
		break;
	case FFloatCurveInitParam::EInitType::InitByValueRange:
		bSuccess = InitByValueRange(CurveAsset
		                            , InInitParam.ValueRange.Bounds.Min
		                            , InInitParam.ValueRange.Bounds.Max);
		break;
	case FFloatCurveInitParam::EInitType::InitByStartEnd_AutoFlipByValue:
		bSuccess = InitByStartEndValues_AutoFlipByValue(CurveAsset
		                                                , InInitParam.ValueRange.StartAndEnd.Start
		                                                , InInitParam.ValueRange.StartAndEnd.End);
		break;
	case FFloatCurveInitParam::EInitType::InitByStartEnd_AutoFlipByTime:
		bSuccess = InitByStartEndValues_AutoFlipByTime(CurveAsset
		                                               , InInitParam.ValueRange.StartAndEnd.Start
		                                               , InInitParam.ValueRange.StartAndEnd.End);
		break;
	default:
		ensureMsgf(false, TEXT("Unsupported InitType %d!"), (int)InInitParam.InitType);
		break;
	}

	if (!ensureMsgf(bSuccess, TEXT("Init failed!")))
	{
		return false;
	}

	if (!ensureMsgf(InitNormalizedCurve(CurveAsset), TEXT("InitNormalizedCurve failed!")))
	{
		return false;
	}

	if (!ensureMsgf(IsValid(), TEXT("self is not valid!")))
	{
		return false;
	}

	return true;
}

bool UFloatCurveWrapper::IsValid() const
{
	auto Curve = GetCurveAsset();
	if (!ensureMsgf(IsCurveValid(GetCurveAsset()), TEXT("Current Curve is not valid!")))
	{
		return false;
	}

	if (!ensureMsgf(IsCurveValid(NormalizedCurve), TEXT("NormalizedCurve is not valid!")))
	{
		return false;
	}

	return true;
}

bool UFloatCurveWrapper::IsCurveValid(UCurveFloat* InCurve) const
{
	if (!ensureMsgf(nullptr != InCurve, TEXT("Curve is not valid!")))
	{
		return false;
	}

	auto AllRealCurves = InCurve->GetCurves();
	if (!ensureMsgf(!AllRealCurves.IsEmpty(), TEXT("Curve has no keys!")))
	{
		return false;
	}

	for (auto& RealCurve : AllRealCurves)
	{
		if (!ensureMsgf(InCurve->IsValidCurve(RealCurve), TEXT("Real Curve is not valid!")))
		{
			return false;
		}
	}

	// 通过 Curve->GetTimeRange() 检查时间范围是否合理
	float MinTime, MaxTime;
	InCurve->GetTimeRange(MinTime, MaxTime);
	if (!ensureMsgf(MinTime < MaxTime, TEXT("Curve time range is not valid!")))
	{
		// 时间范围不合理
		return false;
	}

	// 通过 Curve->GetValueRange() 检查曲线是否合理	
	float MinValue, MaxValue;
	InCurve->GetValueRange(MinValue, MaxValue);
	if (!ensureMsgf(MinValue <= MaxValue, TEXT("Curve value range is not valid!")))
	{
		// 曲线值范围不合理
		return false;
	}

	return true;
}

float UFloatCurveWrapper::GetValueByTime(float InTime, bool bForceCheckValid /*= false*/) const
{
	if (bForceCheckValid)
	{
		// 考虑到性能的因素，只有在函数入参要求强制检测时，才会进行有效性检查
		if (!ensureMsgf(IsValid(), TEXT("self is not valid!")))
		{
			return 0.0f;
		}
	}

	const auto Curve = GetCurveAsset();
	if (!ensureMsgf(nullptr != Curve, TEXT("Curve is not valid!")))
	{
		return 0.0f;
	}

	return Curve->GetFloatValue(InTime);
}

float UFloatCurveWrapper::GetNormalizedValueByTime(float InTime, bool bForceCheckValid) const
{
	if (bForceCheckValid)
	{
		// 考虑到性能的因素，只有在函数入参要求强制检测时，才会进行有效性检查
		if (!ensureMsgf(IsValid(), TEXT("self is not valid!")))
		{
			return 0.0f;
		}
	}

	if (!ensureMsgf(nullptr != NormalizedCurve, TEXT("NormalizedCurve is not valid!")))
	{
		return 0.0f;
	}

	if (!ensureMsgf(0.0f <= InTime && InTime <= 1.0f, TEXT("InTime should be in range [0, 1]!")))
	{
		return 0.0f;
	}

	return NormalizedCurve->GetFloatValue(InTime);
}

UCurveFloat* UFloatCurveWrapper::GetCurveAsset() const
{
	return CurveAsset;
}

bool UFloatCurveWrapper::ChangeTimeRange(UCurveFloat* InCurvePtr, float InNewStartTime, float InNewEndTime)
{
	// 确保 InCurvePtr 已经被正确初始化
	if (!ensureMsgf(InCurvePtr != nullptr, TEXT("InCurvePtr is not valid!")))
	{
		return false;
	}

	// 确保新的起止时间合理
	if (!ensureMsgf(InNewStartTime <= InNewEndTime, TEXT("InNewStartTime must be less than InNewEndTime!")))
	{
		return false;
	}

	// 获取当前曲线的起始和结束时间范围
	float CurrentStartTime, CurrentEndTime;
	InCurvePtr->GetTimeRange(CurrentStartTime, CurrentEndTime);
	if (!ensureMsgf(CurrentStartTime < CurrentEndTime, TEXT("CurrentStartTime must be less than CurrentEndTime!")))
	{
		return false;
	}

	// 如果当前时间范围已经符合要求，则不需要调整
	if (CurrentStartTime == InNewStartTime && CurrentEndTime == InNewEndTime)
	{
		return true;
	}

	// 获取当前所有关键帧的副本
	auto AllKeys = InCurvePtr->FloatCurve.GetCopyOfKeys();

	// 计算时间缩放因子和基线偏移量
	float ScaleFactor = (InNewEndTime - InNewStartTime) / (CurrentEndTime - CurrentStartTime);
	float BaseTime = InNewStartTime - ScaleFactor * CurrentStartTime;

	// 遍历所有关键帧并调整时间戳
	for (FRichCurveKey& Key : AllKeys)
	{
		// 映射旧的时间戳到新的时间范围
		Key.Time = ScaleFactor * Key.Time + BaseTime;
	}

	// 更新曲线的关键帧
	InCurvePtr->FloatCurve.SetKeys(AllKeys);

	return true;
}

bool UFloatCurveWrapper::InitNormalizedCurve(const UCurveFloat* InOriginalCurvePtr)
{
	// 本函数的处理逻辑：
	// 根据 InOriginalCurvePtr 的各个关键帧，生成一条全新的曲线，存储在成员变量 NormalizedCurve 中。
	// 并且 NormalizedCurve 曲线的起和止的值，限定在0和1这样的“标准化”性质的数值区间内；
	// 也就是说， NormalizedCurve 的起和止的值可以分别是0和1，也可以分别是1和0
	// 而曲线中间的部分，允许超出这个区间。比如值为1.2、-0.5等。
	// 因此本函数将会调用 InitByStartEndValues_AutoFlipByValue 函数进行处理，而非 InitByValueRange 函数。
	// 另外，不论 InOriginalCurvePtr 的原有时长是多少，在 NormalizedCurve 中都拉长或缩短为1秒

	if (!ensureMsgf(nullptr != InOriginalCurvePtr, TEXT("InOriginalCurvePtr is not valid!")))
	{
		return false;
	}

	// 生成标准化曲线
	NormalizedCurve = Cast<UCurveFloat>(StaticDuplicateObject(InOriginalCurvePtr, this));
	if (!ensureMsgf(nullptr != NormalizedCurve, TEXT("NormalizedCurve is not valid!")))
	{
		return false;
	}

	// 获取曲线的当前起止值
	float StartValueInCurve = NormalizedCurve->FloatCurve.GetFirstKey().Value;
	float EndValueInCurve = NormalizedCurve->FloatCurve.GetLastKey().Value;
	if (0.0f <= StartValueInCurve && StartValueInCurve <= 1.0f
		&& 0.0f <= EndValueInCurve && EndValueInCurve <= 1.0f)
	{
		// 已经符合要求，不需要调整
		return true;
	}

	// 将起、止关键帧的值，拉到[0, 1]的范围
	bool bSuccess = false;
	if (StartValueInCurve == EndValueInCurve)
	{
		// 起、止的值相等，这种情况下不好计算缩放比率。简单起见，把起、止的值都拉到0.5
		bSuccess = InitByStartEndValues_AutoFlipByValue(NormalizedCurve, 0.5f, 0.5f);
	}
	else
	{
		// 对起、止值进行标准化操作，确保它们分别是“0、1”,或者“1、0”
		float ScaleFactor = 1.0f / (EndValueInCurve - StartValueInCurve);
		ScaleFactor = FMath::Abs(ScaleFactor);

		float MinValue = FMath::Min(StartValueInCurve, EndValueInCurve);

		float NewStartValue = (StartValueInCurve - MinValue) * ScaleFactor;
		float NewEndValue = (EndValueInCurve - MinValue) * ScaleFactor;
		bSuccess = InitByStartEndValues_AutoFlipByValue(NormalizedCurve, NewStartValue, NewEndValue);
	}

	if (!ensureMsgf(bSuccess, TEXT("Init NormalizedCurve value failed!")))
	{
		return false;
	}

	// 将整条曲线的时间，拉到[0, 1]的范围
	bSuccess = ChangeTimeRange(NormalizedCurve, 0.0f, 1.0f);
	if (!ensureMsgf(bSuccess, TEXT("Change NormalizedCurve time range failed!")))
	{
		return false;
	}

	return true;
}

bool UFloatCurveWrapper::InitWithoutChange()
{
	// 不做改变的话，目前暂时不需要做别的处理。
	return true;
}

bool UFloatCurveWrapper::InitByValueRange(UCurveFloat* InCurvePtr
                                          , const float InMinValue
                                          , const float InMaxValue)
{
	if (!ensureMsgf(InMinValue <= InMaxValue, TEXT("InMinValue should be less than InMaxValue!")))
	{
		return false;
	}

	if (!ensureMsgf(nullptr != InCurvePtr, TEXT("InCurvePtr clone failed!")))
	{
		return false;
	}

	// 调整曲线数值的上下限
	float MinValueInCurve, MaxValueInCurve;
	InCurvePtr->GetValueRange(MinValueInCurve, MaxValueInCurve);
	ensure(MinValueInCurve <= MaxValueInCurve);

	if (MinValueInCurve == InMinValue && MaxValueInCurve == InMaxValue)
	{
		return true;
	}

	// 如果曲线数值的上下限与 InMinValue, InMaxValue 不一致，则需要调整
	// 此处将会根据 MinValueInCurve 和 MaxValueInCurve 的范围，以及 InMinValue 和 InMaxValue 的范围，调整曲线数值的上下限
	for (auto It = InCurvePtr->FloatCurve.GetKeyIterator(); It; ++It)
	{
		const FRichCurveKey& KeyOfCurve = *It;
		float OldValue = KeyOfCurve.Value;
		float NewValue = FMath::GetMappedRangeValueClamped(
			FVector2D(MinValueInCurve, MaxValueInCurve)
			, FVector2D(InMinValue, InMaxValue)
			, OldValue);

		if (NewValue == OldValue)
		{
			continue;
		}

		InCurvePtr->FloatCurve.UpdateOrAddKey(KeyOfCurve.Time, NewValue);
	}

	return true;
}

bool UFloatCurveWrapper::InitByStartEndValues_AutoFlipByValue(UCurveFloat* InCurvePtr
                                                              , float InStartValue
                                                              , float InEndValue)
{
	// 确保 InCurvePtr 已经被正确初始化
	if (!ensureMsgf(InCurvePtr != nullptr, TEXT("InCurvePtr is not valid!")))
	{
		return false;
	}

	// 获取曲线的当前起止值
	float StartValueInCurve = InCurvePtr->FloatCurve.GetFirstKey().Value;
	float EndValueInCurve = InCurvePtr->FloatCurve.GetLastKey().Value;

	// 如果曲线的当前起始和结束值已经符合要求，则不需要调整
	if (StartValueInCurve == InStartValue && EndValueInCurve == InEndValue)
	{
		return true;
	}

	// 处理起始值和终止值相同的情况
	if (StartValueInCurve == EndValueInCurve)
	{
		// 如果起始值和终止值相同，而目标起始和终止值不同，这是一个无法解决的情况，因为没有有效的缩放因子
		if (InStartValue != InEndValue)
		{
			ensureMsgf(false, TEXT("StartValueInCurve == EndValueInCurve, but InStartValue != InEndValue!"));
			return false; // 无法进行合理的调整
		}

		// 起始和终止值相同，计算差值偏移
		float Offset = InStartValue - StartValueInCurve;

		// 遍历每个关键帧，平移其值
		for (auto It = InCurvePtr->FloatCurve.GetKeyIterator(); It; ++It)
		{
			const FRichCurveKey& KeyOfCurve = *It;
			float NewValue = KeyOfCurve.Value + Offset;

			// 更新关键帧的值
			InCurvePtr->FloatCurve.UpdateOrAddKey(KeyOfCurve.Time, NewValue);
		}

		return true;
	}

	// 如果曲线中的起始值和终止值之间的“大小关系”，与函数入参指定的“起始值和终止值”之间的“大小关系”不一致，
	// 那么进行翻转。这样可以保持曲线的变化规则，并且曲线的起始和终止值都符合函数入参的要求。
	bool bReverse = false;
	if ((InEndValue - InStartValue) * (EndValueInCurve - StartValueInCurve) < 0.0f)
	{
		bReverse = true;
	}

	// 计算缩放因子和基线偏移量
	float ScaleFactor = (InEndValue - InStartValue) / (EndValueInCurve - StartValueInCurve);
	ScaleFactor = FMath::Abs(ScaleFactor);

	float BaseValue = bReverse ? InEndValue : InStartValue;
	BaseValue -= ScaleFactor * StartValueInCurve;

	// 遍历曲线的所有关键帧，调整它们的值
	for (auto It = InCurvePtr->FloatCurve.GetKeyIterator(); It; ++It)
	{
		const FRichCurveKey& KeyOfCurve = *It;
		float OldValue = KeyOfCurve.Value;

		// 映射旧值到新的起始和结束值范围
		float NewValue = ScaleFactor * OldValue + BaseValue;

		// 如果需要翻转，则在调整后反转值
		if (bReverse)
		{
			NewValue = InStartValue + InEndValue - NewValue;
		}

		// 更新关键帧的值
		InCurvePtr->FloatCurve.UpdateOrAddKey(KeyOfCurve.Time, NewValue);
	}

	return true;
}

bool UFloatCurveWrapper::InitByStartEndValues_AutoFlipByTime(UCurveFloat* InCurvePtr
                                                             , float InStartValue
                                                             , float InEndValue)
{
	// 确保 InCurvePtr 已经被正确初始化
	if (!ensureMsgf(InCurvePtr != nullptr, TEXT("InCurvePtr is not valid!")))
	{
		return false;
	}

	// 获取曲线的当前起止值
	float StartValueInCurve = InCurvePtr->FloatCurve.GetFirstKey().Value;
	float EndValueInCurve = InCurvePtr->FloatCurve.GetLastKey().Value;

	// 如果曲线的当前起始和结束值已经符合要求，则不需要调整
	if (StartValueInCurve == InStartValue && EndValueInCurve == InEndValue)
	{
		return true;
	}

	// 获取曲线的时间范围
	float MinTime, MaxTime;
	InCurvePtr->GetTimeRange(MinTime, MaxTime);

	// 检查是否需要翻转时间顺序
	bool bReverse = ((InEndValue - InStartValue) * (EndValueInCurve - StartValueInCurve) < 0.0f);

	// 获取所有关键帧的副本
	auto AllKeys = InCurvePtr->FloatCurve.GetCopyOfKeys();

	if (StartValueInCurve == EndValueInCurve)
	{
		// 如果起始值和终止值相同的情况下，只平移关键帧的值
		if (InStartValue != InEndValue)
		{
			ensureMsgf(false, TEXT("StartValueInCurve == EndValueInCurve, but InStartValue != InEndValue!"));
			return false;
		}

		float Offset = InStartValue - StartValueInCurve;
		for (FRichCurveKey& Key : AllKeys)
		{
			Key.Value += Offset;
		}
	}
	else
	{
		// 计算缩放因子和基线偏移量
		float ScaleFactor = (InEndValue - InStartValue) / (EndValueInCurve - StartValueInCurve);
		ScaleFactor = FMath::Abs(ScaleFactor);

		float BaseValue = bReverse ? InEndValue : InStartValue;
		BaseValue -= ScaleFactor * StartValueInCurve;

		// 调整并翻转关键帧的时间戳和值
		for (FRichCurveKey& Key : AllKeys)
		{
			// 映射旧值到新的范围
			Key.Value = ScaleFactor * Key.Value + BaseValue;

			// 如果需要翻转时间戳
			if (bReverse)
			{
				Key.Time = MaxTime - (Key.Time - MinTime);
			}
		}
	}

	if (bReverse)
	{
		// 如果需要翻转，那么应该对Key根据时间戳进行排序，从而符合Unreal本身的规范
		AllKeys.StableSort([](const FRichCurveKey& KeyLhs, const FRichCurveKey& KeyRhs)
		{
			return KeyLhs.Time < KeyRhs.Time;
		});
	}

	// 更新曲线的关键帧
	InCurvePtr->FloatCurve.SetKeys(AllKeys);

	return true;
}
